#include "car.h"
inline long int get_current_time() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000; 
}

Car* create_car(int carid, Tunnel *tn) {
    Car *c = (Car*)malloc(sizeof(Car));
    c->carid = carid;
    c->tn = tn;
    c->enter = 0;
    c->operation = NULL;
    c->op_count = 0;
    memset(c->car_mem, 0, sizeof(c->car_mem));
    c->read_p = (int*)calloc(tn->mail_sum, sizeof(int));
    return c;
}

void enter_tunnel(Car *c) {
   // 更新等待车辆数
    Wait(c->tn->light_semid, 0);
    c->tn->waiting_counts[c->direction]++;
    Signal(c->tn->light_semid, 0);

    while(1)
    {
        Wait(c->tn->light_semid, 0);
        
        // 检查是否允许进入：
        // - 当前方向与交通灯方向一致
        // - 隧道车流方向与交通灯方向一致
        // - 隧道总容量未满
        if (*c->tn->traffic_light == c->direction && *c->tn->car_in < c->tn->max_car_in) {
            if(*c->tn->current_direction == c->direction || *c->tn->current_direction == -1){
                Signal(c->tn->light_semid, 0);
                *c->tn->current_direction = c->direction;
                break;
            }
        }
        #ifdef DEBUG
        //printf("[Car %d]: The car sees light %d,wait for %d\n", c->carid, *c->tn->traffic_light, c->direction);
        //printf("[Car %d]: wait after %d %d\n", c->carid, *c->tn->car_in, *c->tn->current_direction);
        #endif
        Signal(c->tn->light_semid, 0);
        struct timespec ts;
        ts.tv_sec = 0;
        ts.tv_nsec = 100000000; // 100ms
        nanosleep(&ts, NULL); // 短暂休眠避免忙等待
    }
    
    Wait(c->tn->tun_car_semid, 0);
    printf("[Car %d]: The car is entering the tunnel.\n", c->carid);
    (*c->tn->car_in)++;
    
    c->enter = get_current_time();
    Wait(c->tn->light_semid, 0);
    c->tn->waiting_counts[c->direction]--; // 进入后减少等待计数 
    Signal(c->tn->light_semid, 0);
}
void leave_tunnel(Car *c, int i) {
    // 1. 减少当前方向车辆计数
    Wait(c->tn->tun_dir_semid, 0);
    (*c->tn->car_in)--;
    #ifdef DEBUG
    printf("[Car %d]: sees %d car in tunnel\n", c->carid, *c->tn->car_in);
    #endif
    if (*c->tn->car_in == 0) {
        int opposite_dir = 1 - (*c->tn->current_direction);
        printf("[Car %d]: Last car in direction %d. Tunnel may switch.\n", 
               c->carid, *c->tn->current_direction);
        if (c->tn->waiting_counts[opposite_dir] > 0) {
            *c->tn->current_direction = opposite_dir;
            printf("[Direction] Switched to %d (opposite has %d waiting cars)\n",
                  opposite_dir, c->tn->waiting_counts[opposite_dir]);
        }
        else{
            *c->tn->current_direction = -1; //自由通行
        }
    }
    Signal(c->tn->tun_dir_semid, 0);

    // 2. 释放隧道容量
    Signal(c->tn->tun_car_semid, 0);

    // 3. 打印车辆内存（同单向逻辑）
    if (strlen(c->car_mem)) {
        printf("[Car %d]: Memory content: %s\n", c->carid, c->car_mem);
    } else {
        printf("[Car %d]: Memory is empty.\n", c->carid);
    }

    // 4. 释放资源
    for (; i < c->op_count; i++) {
        #ifdef DEBUG 
        printf("operation %d ", i);
        #endif
        if (strcmp(c->operation[i].op, "w") == 0) {
            printf("[Car %d]: The car is outside the tunnel writing.\n", c->carid);
        } else {
            printf("[Car %d]: The car is outside the tunnel reading.\n", c->carid);
        }
    }
    free(c->read_p);
    free(c->operation);
    free(c);
}

void read_from_maibox(Car *c, int mid, int length, int lasttime) {
    Wait(c->tn->mail_semid[mid], 2);
    Wait(c->tn->mail_semid[mid], 4);
    Wait(c->tn->mail_semid[mid], 0);

    int* read = (int*)get_shm(c->tn->readcount);
    read[mid]++;

    if (read[mid] == 1) {
        Wait(c->tn->mail_semid[mid], 3);
    }
    Signal(c->tn->mail_semid[mid], 0);
    Signal(c->tn->mail_semid[mid], 4);
    Signal(c->tn->mail_semid[mid], 2);

    usleep(lasttime * 1000);

    char* message = (char*)get_shm(c->tn->mail_shmid[mid]);
    char pre_read[100];
    if (c->read_p[mid] + length > strlen(message)) {
        strncat(c->car_mem, message + c->read_p[mid], strlen(message) - c->read_p[mid]);
        strncpy(pre_read, message + c->read_p[mid], strlen(message) - c->read_p[mid]);
        pre_read[strlen(message) - c->read_p[mid]] = '\0';
        printf("[Car %d]: The car reached the end of the mailbox[%d], can't read more!\n", c->carid, mid);
    } else {
        strncat(c->car_mem, message + c->read_p[mid], length);
        strncpy(pre_read, message + c->read_p[mid], length);
        pre_read[length] = '\0';
    }
    c->read_p[mid] = strlen(message);
    disconnect_shm(message);

    Wait(c->tn->mail_semid[mid], 0);
    read[mid]--;
    if (read[mid] == 0) {
        Signal(c->tn->mail_semid[mid], 3);
    }
    Signal(c->tn->mail_semid[mid], 0);
    disconnect_shm(read);

    printf("[Car %d]: The car is succeeding in reading \"%s\" from mailbox[%d]\n", c->carid, pre_read, mid);
}

void write_to_mailbox(Car *c, char *context, int mid, int lasttime) {
    Wait(c->tn->mail_semid[mid], 1);

    int* write = (int*)get_shm(c->tn->writecount);
    write[mid]++;
    if (write[mid] == 1) {
        Wait(c->tn->mail_semid[mid], 4);
    }
    Signal(c->tn->mail_semid[mid], 1);
    Wait(c->tn->mail_semid[mid], 3);

    usleep(lasttime * 1000);

    char* message = (char*)get_shm(c->tn->mail_shmid[mid]);
    strcat(message, context);
    disconnect_shm(message);

    Signal(c->tn->mail_semid[mid], 3);
    Wait(c->tn->mail_semid[mid], 1);
    write[mid]--;
    if (write[mid] == 0) {
        Signal(c->tn->mail_semid[mid], 4);
    }
    Signal(c->tn->mail_semid[mid], 1);
    disconnect_shm(write);

    printf("[Car %d]: The car is succeeding in writing \"%s\" to mailbox[%d]\n", c->carid, context, mid);
}

void exec_car(Car *c) {
    enter_tunnel(c);

    int i;
    for (i = 0; i < c->op_count; i++) {
        long int now = get_current_time();
        if ((now - c->enter) >= c->tn->travel_time) {
            break;
        }
        if (strcmp(c->operation[i].op, "w") == 0) {
            if (i == 0) {
                int duration = c->operation[i].duration - (now - c->enter);
                write_to_mailbox(c, c->operation[i].content, c->operation[i].mid, duration);
            } else {
                write_to_mailbox(c, c->operation[i].content, c->operation[i].mid, c->operation[i].duration);
            }
        } else {
            int length = atoi(c->operation[i].content);
            if (i == 0) {
                int duration = c->operation[i].duration - (now - c->enter);
                read_from_maibox(c, c->operation[i].mid, length, duration);
            } else {
                read_from_maibox(c, c->operation[i].mid, length, c->operation[i].duration);
            }
        }
    }
    long int now = get_current_time();
    if (now - c->enter < c->tn->travel_time) {
        usleep((c->tn->travel_time - (now - c->enter)) * 1000);
    }

    leave_tunnel(c,i);
}
